/**
 * @author Ahammer
 * @date   2009 05
 * @update 2010 09 added the handling if number of bands does not match 
 * @update 2011 01 added 16 bit support
 */

/*
 * This file is part of Iqm.
 * Copyright (c) 2010-2011 Helmut Ahammer
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

package op;

import java.awt.RenderingHints;
import java.awt.image.DataBuffer;
import java.awt.image.renderable.ParameterBlock;
import javax.media.jai.JAI;
import javax.media.jai.ParameterBlockJAI;
import javax.media.jai.PlanarImage;
import javax.media.jai.RenderedOp;

import main.Board;
import main.IqmTools;

/**
 * This is the main image processing class
 * There exist two approaches:
 * A user defined JAI operator is just called 
 * or:
 * The actual processing is implemented in this class 
 */
public class IqmCalcImageOperator{ 


  public IqmCalcImageOperator() {
	  //WARNING: Don't declare fields here
	  //Fields declared here aren't thread safe!   
  }
  /**
   * 
   * @param ParametrBlockJAI pb
   */
  public Object run(ParameterBlockJAI pbJAI){
	  Object ob = null;
	  //ob = JAI.create("IqmCalcImage".toLowerCase(), pb, null);  
	  
	  PlanarImage pi1 = (PlanarImage) pbJAI.getSource(0);
	  PlanarImage pi2 = (PlanarImage) pbJAI.getSource(1);
	  int calc 	 		= pbJAI.getIntParameter(0);
	  int resultOptions = pbJAI.getIntParameter(1);
	  
	  String type1        = IqmTools.getImgTyp(pi1);
	  String type2        = IqmTools.getImgTyp(pi2);
//	  if (type1 != type2) {
//		  Board.appendTexln("IqmCalcImageOperator: Image types do not fit together!");
//		  return null;
//	  }
	  double typeGreyMax1 = IqmTools.getImgTypeGreyMax(pi1);
	  double typeGreyMax2 = IqmTools.getImgTypeGreyMax(pi2);
	  
	  //RenderingHints rh = new RenderingHints(JAI.KEY_BORDER_EXTENDER, BorderExtender.createInstance(BorderExtender.BORDER_COPY));	
	  RenderingHints rh = null;	
	  ParameterBlock pb = new ParameterBlock();

	  if ((calc == 0)||(calc == 1)||(calc == 2)||(calc == 3)||(calc == 10)||(calc == 11)){
		  pb.addSource(pi1);
		  pb.add(DataBuffer.TYPE_DOUBLE);
		  pi1 = JAI.create("format", pb);  
	
	      pb.removeSources();
		  pb.addSource(pi2);
		  pb.add(DataBuffer.TYPE_DOUBLE);
		  pi2 = JAI.create("format", pb);  
	  }
	  
	  pb.removeSources();
	  pb.removeParameters();
	
	  //if number of bands in each image does not match
	  int pi1NumBands = pi1.getNumBands();
	  int pi2NumBands = pi2.getNumBands();	  
	  if (pi2NumBands == 1 && pi2NumBands < pi1NumBands){ //second image less bands than first image
			ParameterBlockJAI pbMerge = new ParameterBlockJAI("bandmerge");
			for(int n = 0; n < pi1NumBands; n++){
				pbMerge.setSource(pi2, n);
			}
			pi2 = JAI.create("bandmerge", pbMerge, null);	//	
	  }
	  
	  pb.removeSources();
	  pb.removeParameters();
	  	  
	  if (calc == 0){ //Add
		  pb.addSource(pi1);	
		  pb.addSource(pi2);	
		  ob= JAI.create("add", pb, rh);
	  }
	  if (calc == 1){ //Subtract
		  pb.addSource(pi1);	
		  pb.addSource(pi2);	
		  ob= JAI.create("Subtract", pb, rh);
	  }
	  if (calc == 2){ //Multiply
		  pb.addSource(pi1);	
		  pb.addSource(pi2);	
		  ob= JAI.create("Multiply", pb, rh);
	  }
	  if (calc == 3){ //Divide
		  pb.addSource(pi1);	
		  pb.addSource(pi2);	
		  ob= JAI.create("Divide", pb, rh);
	  }
	  if (calc == 4){ //AND
		  pb.addSource(pi1);	
		  pb.addSource(pi2);	
		  ob= JAI.create("And", pb, rh);
	  }
	  if (calc == 5){ //OR
		  pb.addSource(pi1);	
		  pb.addSource(pi2);	
		  ob= JAI.create("Or", pb, rh);
	  }
	  if (calc == 6){ //XOR
		  pb.addSource(pi1);	
		  pb.addSource(pi2);	
		  ob= JAI.create("Xor", pb, rh);
	  }
	  if (calc == 6){ //XOR
		  pb.addSource(pi1);	
		  pb.addSource(pi2);	
		  ob= JAI.create("Xor", pb, rh);
	  }
	  if (calc == 7){ //Min
		  pb.addSource(pi1);	
		  pb.addSource(pi2);	
		  ob= JAI.create("Min", pb, rh);
	  }
	  if (calc == 8){ //Max
		  pb.addSource(pi1);	
		  pb.addSource(pi2);	
		  ob= JAI.create("Max", pb, rh);
	  }
	  if (calc == 9){ //Average
		  pb.addSource(pi1);	
		  pb.addSource(pi2);	
		  PlanarImage pi3 = JAI.create("Add", pb, null);
		  
		  pb.removeSources();
		  pb.removeParameters();
		  pb.addSource(pi3);
		  pb.add(new double[]{2,2,2});
		  ob= JAI.create("DivideByConst", pb, null);
		  
	  }
	  if (calc == 10){ //Difference
		  pb.addSource(pi1);	
		  pb.addSource(pi2);	
		  PlanarImage pi3 = JAI.create("Subtract", pb, null);
		  
		  pb.removeSources();
		  pb.removeParameters();
		  pb.addSource(pi3);
		  ob= JAI.create("Absolute", pb, null);
	  }
	  if (calc == 11){ //ROI
		  pb.addSource(pi2);
		  pb.add(new double[]{typeGreyMax2, typeGreyMax2, typeGreyMax2});  //{255, 255, 255}
		  PlanarImage pi3= JAI.create("DivideByConst", pb, null);
		  
		  pb.removeSources();
		  pb.removeParameters();
		  pb.addSource(pi1);
		  pb.addSource(pi3);
		  ob = JAI.create("Multiply", pb, null); //multiply with [0,1] 
			  }  
	  
	  //--------------------------------------------------------------------
	  if (resultOptions == 0){ //clamp to image type
	      pb.removeSources();
	      pb.removeParameters();
		  pb.addSource(ob);
		  if (type1 == "RGB")    pb.add(DataBuffer.TYPE_BYTE);
		  if (type1 == "8 bit")  pb.add(DataBuffer.TYPE_BYTE);
		  if (type1 == "16 bit") pb.add(DataBuffer.TYPE_USHORT);
		  ob = JAI.create("format", pb);  	  
	  }
	  if (resultOptions == 1){ //normalize to byte	  
		  pb.removeSources();
		  pb.removeParameters();
		  pb.addSource(ob);
		  RenderedOp extrema = JAI.create("extrema", pb);
		  double[] minVec = (double[])extrema.getProperty("minimum");
		  double[] maxVec = (double[])extrema.getProperty("maximum");
		  double min = minVec[0];
		  double max = maxVec[0];
		
		  pb.removeSources();
		  pb.removeParameters();
		  pb.addSource(ob);
		  pb.add(new double[] {(typeGreyMax1/(max-min))}); //Rescale 
		  pb.add(new double[] {((typeGreyMax1*min)/(min-max))}); //offset
		  ob = JAI.create("rescale", pb); 
		 
		  pb.removeSources();
		  pb.removeParameters();
		  pb.addSource(ob);
		  if (type1 == "RGB")    pb.add(DataBuffer.TYPE_BYTE);
		  if (type1 == "8 bit")  pb.add(DataBuffer.TYPE_BYTE);
		  if (type1 == "16 bit") pb.add(DataBuffer.TYPE_USHORT);
		  ob = JAI.create("format", pb);  	 
	  }

	  if (resultOptions == 2){ //Actual
		  //does nothing
	  }
	  return ob;
  }
}
